﻿#region License
//
//  Copyright (c) 2012 Shawn Rakowski
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy of 
//  this software and associated documentation files (the "Software"), to deal in the 
//  Software without restriction, including without limitation the rights to use, copy, 
//  modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
//  and to permit persons to whom the Software is furnished to do so, subject to the 
//  following conditions:
//
//  The above copyright notice and this permission notice shall be included in all 
//  copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
//  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
//  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
//  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ERMAHGERD
{
    public class Translate
    {
        public static string ToErMahGerd(string text)
        {
            List<Token> tokens = SplitIntoTokens(text);
            Parallel.ForEach(tokens, token =>
            {
                ConvertTokenToErmahgerd(token);
            });
            return string.Concat(tokens);
        }

        private static List<Token> SplitIntoTokens(string plainText)
        {
            List<Token> tokens = new List<Token>();
            if (plainText.Length == 0)
                return tokens;

            var chars = plainText.ToCharArray();
            Token currentToken = null;
            for (int idx = 0; idx < chars.Length; idx++)
            {
                var c = chars[idx];

                if (idx == 0)
                    if (char.IsWhiteSpace(c) || char.IsPunctuation(c))
                        currentToken = new WhitespaceToken();
                    else
                        currentToken = new WordToken();

                if (char.IsWhiteSpace(c) || char.IsPunctuation(c) && currentToken is WordToken)
                {
                    tokens.Add(currentToken);
                    currentToken = new WhitespaceToken();
                }
                else if (!(char.IsWhiteSpace(c) || char.IsPunctuation(c)) && currentToken is WhitespaceToken)
                {
                    tokens.Add(currentToken);
                    currentToken = new WordToken();
                }

                currentToken.Value += c;
            }
            tokens.Add(currentToken);
            return tokens;
        }

        private static Dictionary<string, string> SpecialWords
        {
            get
            {
                if (_specialWords == null)
                {
                    _specialWords = new Dictionary<string, string>();
                    _specialWords["THEY"] = "DEY";
                    _specialWords["THE"] = "DA";
                    _specialWords["GOOSEBUMPS"] = "GERSBERMS";
                    _specialWords["FAVORITE"] = "FRAVRIT";
                    _specialWords["YOU"] = "U";
                    _specialWords["U"] = "U";
                    _specialWords["OMG"] = "ERMAHGERD";
                    _specialWords["ERMAHGERD"] = "ERMAHGERD";
                }
                return _specialWords;
            }
        }
        private static Dictionary<string, string> _specialWords = null;

        private static void ConvertTokenToErmahgerd(Token token)
        {
            if (token is WhitespaceToken)
                return;

            token.Value = ConvertWordToErmahgerd(token);
        }

        private static string ConvertWordToErmahgerd(Token token)
        {
            string newVal = token.Value.ToUpper();
            if (newVal.Length == 0)
                return newVal;

            // some words we sub up front.
            if (SpecialWords.ContainsKey(newVal))
            {
                newVal = SpecialWords[newVal];
                return newVal;
            }

            // OH's && OW's are O's
            newVal = newVal.Replace("OH", "O")
                .Replace("OW", "O")
                .Replace("AH", "A")
                .Replace("AW", "A");

            // handle special endings
            string suffix = string.Empty;
            if (newVal.EndsWith("ES") && !newVal.EndsWith("SES") && !newVal.EndsWith("SHES"))
            {
                suffix = "S";
                newVal = newVal.Remove(newVal.Length - 2, 2);
            }
            else if (newVal.EndsWith("ING"))
            {
                suffix = "IN";
                newVal = newVal.Remove(newVal.Length - 3, 3);
            }
            else if (newVal.EndsWith("IE"))
                if (newVal.Length == 2 || (newVal.Length >= 3 && !IsVowel(newVal[newVal.Length - 3])))
                {
                    suffix = "ER";
                    newVal = newVal.Remove(newVal.Length - 2, 2);
                }

            if (newVal.Length == 0)
                return suffix;

            // remove duplicate letters and vowels.
            newVal = RemoveDuplicates(newVal);

            if (newVal.Length == 2 && suffix.Length == 0)
                if (newVal[0] == 'M' && IsVowel(newVal[1], true))
                    return "MAH";

            // drop the last vowel unless A or I
            var lastChar = newVal[newVal.Length - 1];
            if (IsVowel(lastChar) && (newVal.Length > 2))
            {
                var nextToLastChar = char.MinValue;
                if (newVal.Length >= 2)
                    nextToLastChar = newVal[newVal.Length - 2];

                if (suffix.Length == 0 && (lastChar == 'A' || lastChar == 'I' || lastChar == 'O'))
                    suffix = "ER";

                newVal = newVal.Remove(newVal.Length - 1, 1);
            }

            var prevChar = char.MinValue;
            var tempVal = new StringBuilder();
            for (int idx = 0; idx < newVal.Length; idx++)
            {
                var c = newVal[idx];
                var next = char.MinValue;
                if (idx + 1 < newVal.Length)
                    next = newVal[idx + 1];

                if (((IsVowel(c) && (next != 'R' || c == 'A' || c == 'O')) || (c == 'Y' && idx != 0)) &&
                    (prevChar != 'M' || (c == 'A' || c == 'I' || c == 'O' && prevChar == 'M')))
                    tempVal.Append("ER");
                else if (IsVowel(c, true) && prevChar == 'M' && c != 'E')
                    tempVal.Append("AH");
                else
                    tempVal.Append(c);

                prevChar = c;
            }

            newVal = RemoveDuplicates(tempVal.ToString() + suffix);

            return newVal;
        }

        private static string RemoveDuplicates(string newVal)
        {
            List<char> contigVowels = new List<char>();
            var prevChar = char.MinValue;
            StringBuilder tempVal = new StringBuilder();
            foreach (var c in newVal)
            {
                if (c == prevChar)
                    continue;

                if (IsVowel(c) || (c == 'Y' && contigVowels.Count > 0))
                {
                    contigVowels.Add(c);
                    prevChar = c;
                    continue;
                }
                else if (contigVowels.Count > 0)
                {
                    var bestVowel = GetBestVowel(contigVowels);
                    tempVal.Append(bestVowel);
                    contigVowels.Clear();
                }

                tempVal.Append(c);
                prevChar = c;
            }

            if (contigVowels.Count > 0)
            {
                var bestVowel = GetBestVowel(contigVowels);
                tempVal.Append(bestVowel);
                contigVowels.Clear();
            }

            return tempVal.ToString();
        }

        private static char GetBestVowel(List<char> contigVowels)
        {
            var bestVowel = contigVowels.First();
            if (contigVowels.Contains('E'))
                bestVowel = 'E';
            else if (contigVowels.Contains('U'))
                bestVowel = 'U';
            else if (contigVowels.Contains('I'))
                bestVowel = 'I';
            return bestVowel;
        }

        private static bool IsVowel(char value, bool includeY = false)
        {
            return (value == 'A' || value == 'E' ||
                value == 'I' || value == 'O' ||
                value == 'U' || (value == 'Y' && includeY));
        }
    }
}
